home
***
CD-ROM
|
disk
|
FTP
|
other
***
search
/
Libris Britannia 4
/
science library(b).zip
/
science library(b)
/
DDJMAG
/
DDJ9212.ZIP
/
cdrom.asc
< prev
next >
Wrap
Text File
|
1992-11-30
|
24KB
|
687 lines
_INSIDE THE ISO-966 FILESYSTEM FORMAT_
by William Frederick Jolitz and Lynne Greer Jolitz
[LISTING ONE]
/* cdromcheck: A simple program to check what kind of CDROM we have, and to
* list the volume descriptors that are present, if any. */
#include <stdio.h>
#include "primary_descriptor"
#include "directory_entry"
#define VD_LSN 16 /* first logical sector of volume descriptor table */
#define CDROM_LSECSZ 2048 /* initial logical sector size of a CDROM */
char buffer[CDROM_LBS];
static hsffmt, isofmt;
void doiso(struct iso_primary_descriptor *, int);
void dohsf(struct hsf_primary_descriptor *, int);
#define HSF 1
#define ISO 2
int cdromfmt;
char *cdromfmtnames[] = {
"unknown format",
"High Sierra",
"ISO - 9660"
};
char *voltypenames[] = {
"Boot Record",
"Standard File Structure",
"Coded Character Set File Structure",
"Unspecified File Structure",
};
#define NVOLTYPENAMES (sizeof(voltypenames)/sizeof(char *))
int
main(int argc, char *argv[])
{
struct iso_primary_descriptor *ipd;
struct hsf_primary_descriptor *hpd;
int cdfd;
cdfd = open("/dev/ras2d", 0);
/* locate at the beginning of the descriptor table */
lseek(cdfd, VD_LSN*CDROM_LSECSZ, SEEK_SET);
ipd = (struct iso_primary_descriptor *) buffer;
hpd = (struct hsf_primary_descriptor *) buffer;
/* walk descriptor table */
for(;;) {
unsigned char type;
read(cdfd, buffer, sizeof(buffer));
/* determine ISO or HSF format of CDROM */
if (cdromfmt == 0) {
if (strncmp (ipd->id, ISO_STANDARD_ID, sizeof(ipd->id)) == 0)
cdromfmt = ISO;
if (strncmp (hpd->id, HSF_STANDARD_ID, sizeof(hpd->id)) == 0)
cdromfmt = HSF;
if (cdromfmt)
printf("%s Volume Descriptors:\n", cdromfmtnames[cdromfmt]);
else {
printf("%s\n", cdromfmtnames[0]);
exit(0);
}
}
/* type of descriptor */
if (cdromfmt == ISO)
type = (unsigned char)ipd->type[0];
else
type = (unsigned char)hpd->type[0];
/* type of volume */
if (type < NVOLTYPENAMES)
printf("\t%s\n", voltypenames[type]);
else if (type != VD_END)
printf("\t Reserved - %d\n", type);
/* terminating volume */
if (type == VD_END)
break;
/* ISO 9660 filestructure */
if (cdromfmt == ISO && type == VD_PRIMARY
&& strncmp (ipd->id, ISO_STANDARD_ID, sizeof(ipd->id)) == 0) {
doiso(ipd, cdfd);
isofmt++;
continue;
}
/* (obselete) High Sierra filestructure */
if (cdromfmt == HSF && type == VD_PRIMARY
&& strncmp (hpd->id, HSF_STANDARD_ID, sizeof(hpd->id)) == 0) {
dohsf(hpd, cdfd);
hsffmt++;
continue;
}
printf("\n");
}
return (0);
}
char *iso_astring(char *, int len);
/* rude translation routines for interpreting strings, words, halfwords */
#define ISO_AS(s) (iso_astring(s, sizeof(s)))
#define ISO_WD(s) (*(unsigned *)(s))
#define ISO_HWD(s) (*(unsigned short *)(s))
/* dig out the details of a ISO - 9660 descriptor */
void
doiso(struct iso_primary_descriptor *ipd, int fd) {
printf(" Volume ID:\t\t%s\n", ISO_AS(ipd->volume_id));
printf(" Logical Block Size:\t%d\n", ISO_HWD(ipd->logical_block_size));
printf(" Volume Set ID:\t\t%s\n", ISO_AS(ipd->volume_set_id));
printf(" Publisher ID:\t\t%s\n", ISO_AS(ipd->publisher_id));
printf(" Preparer ID:\t\t%s\n", ISO_AS(ipd->preparer_id));
printf(" Application ID:\t\t%s\n", ISO_AS(ipd->application_id));
printf(" Copyright File ID:\t%s\n", ISO_AS(ipd->copyright_file_id));
printf(" Abstract File ID:\t%s\n", ISO_AS(ipd->abstract_file_id));
printf(" Bibliographic File ID:\t%s\n", ISO_AS(ipd->bibliographic_file_id));
printf(" Creation Date:\t\t%s\n", ISO_AS(ipd->creation_date));
printf(" Modification Date:\t%s\n", ISO_AS(ipd->modification_date));
printf(" Expiration Date:\t%s\n", ISO_AS(ipd->expiration_date));
printf(" Effective Date:\t%s\n", ISO_AS(ipd->effective_date));
}
/* dig out the details of a High Sierra Descriptor */
void
dohsf(struct hsf_primary_descriptor *hpd, int fd) {
printf(" Volume Logical Block Number:\t%d\n", ISO_WD(hpd->volume_lbn));
printf(" Volume ID:\t\t%s\n", ISO_AS(hpd->volume_id));
printf(" Logical Block Size:\t%d\n", ISO_HWD(hpd->logical_block_size));
printf(" Volume Set ID:\t\t%s\n", ISO_AS(hpd->volume_set_id));
printf(" Publisher ID:\t\t%s\n", ISO_AS(hpd->publisher_id));
printf(" Preparer ID:\t\t%s\n", ISO_AS(hpd->preparer_id));
printf(" Application ID:\t\t%s\n", ISO_AS(hpd->application_id));
printf(" Copyright File ID:\t%s\n", ISO_AS(hpd->copyright_file_id));
printf(" Abstract File ID:\t%s\n", ISO_AS(hpd->abstract_file_id));
printf(" Creation Date:\t\t%s\n", ISO_AS(hpd->creation_date));
printf(" Modification Date:\t%s\n", ISO_AS(hpd->modification_date));
printf(" Expiration Date:\t%s\n", ISO_AS(hpd->expiration_date));
printf(" Effective Date:\t%s\n", ISO_AS(hpd->effective_date));
}
static char __strbuf[200];
/* turn a blank padded character feild into the null terminated strings
that POSIX/UNIX/WHATSIX likes so much */
char *iso_astring(char *sp, int len) {
bcopy(sp, __strbuf, len);
__strbuf[len] = 0;
for (sp = __strbuf + len - 1; sp > __strbuf ; sp--)
if (*sp == ' ')
*sp = 0;
return(__strbuf);
}
[LISTING TWO]
From Rich Morin's "Prime Time Freeware", Vol 1.1 CDROM:
ISO - 9660 Volume Descriptors: Standard File Structure
Volume ID: PTF_1_1
Logical Block Size: 2048
Volume Set ID:
Publisher ID:
Preparer ID: MERIDIAN_DATA_CD_PUBLISHER
Application ID:
Copyright File ID:
Abstract File ID:
Bibliographic File ID:
Creation Date: 1992011101452700
Modification Date: 1992011101452700
Expiration Date: 0000000000000000
Effective Date: 1992011101452700
From Discovery System's CD-ROM Sampler:
High Sierra Volume Descriptors:
Standard File Structure
Volume Logical Block Number: 16
Volume ID: CDROM_SAMP1
Logical Block Size: 2048
Volume Set ID: CDROM_SAMP1
Publisher ID: DISCOVERY
Preparer ID: DISCOVERY
Application ID:
Copyright File ID:
Abstract File ID:
Creation Date: 1987111215553400
Modification Date: 1987111215553400
Expiration Date: 0000000000000000
Effective Date: 0000000000000000
Standard File Structure
Volume Logical Block Number: 17
Volume ID: CDROM_SAMP1
Logical Block Size: 2048
Volume Set ID: CDROM_SAMP1
Publisher ID: DISCOVERY
Preparer ID: DISCOVERY
Application ID:
Copyright File ID:
Abstract File ID:
Creation Date: 1987111215553400
Modification Date: 1987111215553400
Expiration Date: 0000000000000000
Effective Date: 0000000000000000
[LISTING THREE]
/* cdromfs.h: various definitions for CDROM filesystems. */
#define VD_LSN 16 /* first logical sector of volume descriptor table */
#define CDROM_LSECSZ 2048 /* initial logical sector size of a CDROM */
#define HSF 1
#define ISO 2
char *cdromfmtnames[] = {
"unknown format",
"High Sierra",
"ISO - 9660"
};
char *voltypenames[] = {
"Boot Record",
"Standard File Structure",
"Coded Character Set File Structure",
"Unspecified File Structure",
};
/* rude translation routines for interpreting strings, words, halfwords */
#define ISO_AS(s) (iso_astring(s, sizeof(s)))
#define ISO_WD(s) (*(unsigned *)(s))
#define ISO_HWD(s) (*(unsigned short *)(s))
#define ISO_BY(s) (*(unsigned char *)(s))
#define NVOLTYPENAMES (sizeof(voltypenames)/sizeof(char *))
struct cdromtime {
unsigned char years; /* number of years since 1900 */
unsigned char month; /* month of the year */
unsigned char day; /* day of month */
unsigned char hour; /* hour of day */
unsigned char min; /* minute of the hour */
unsigned char sec; /* second of the minute */
unsigned char tz; /* timezones, in quarter hour increments */
/* or, longitude in 3.75 of a degree */
};
#define CD_FLAGBITS "vdaEp m" /* file flag bits */
/* Handy macro's for block calculation */
#define lbntob(fs, n) ((fs)->lbs * (n))
#define btolbn(fs, n) ((fs)->lbs * (n))
#define trunc_lbn(fs, n) ((n) - ((n) % (fs)->lbs)
#define roundup(n, d) ((((n) + (d)) / (d)) * (d))
[LISTING FOUR]
/* cdromcat -- A simple program to interpret the CDROM filesystem, and return.
* the contents of the file (directories are formatted and printed, files are
* returned untranslated). */
#include <sys/types.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <time.h>
#include "primary_descriptor"
#include "directory_entry"
#include "cdromfs.h"
/* per filesystem information */
struct fs {
char *name; /* kind of cdrom filesystem */
int fd; /* open file descriptor */
int lbs; /* logical block size */
int type; /* which flavor */
} fsd;
/* filesystem directory entry */
struct directent {
union fsdir {
struct iso_directory_record iso_dir;
struct hsf_directory_record hsf_dir;
} fsd;
/* actually, name contains name, reserved field, and extensions area */
char name[255 - sizeof(union fsdir)];
} rootent;
/* filesystem volume descriptors */
union voldesc {
struct iso_primary_descriptor iso_desc;
struct hsf_primary_descriptor hsf_desc;
};
char *iso_astring(char *, int len);
char *cdrom_time(struct cdromtime *, int);
void printdirent(struct directent *, struct fs *);
void printdirents(struct directent *, struct fs *);
void printdirentheader(char *p);
int searchdirent(struct directent *, struct directent *, struct directent *,
struct fs *);
void extractdirent(struct directent *, struct fs *);
int lookup(struct directent *, struct directent *, char *, struct fs *);
/* "fetch directory value" */
#define FDV(b, f, t) (((t) == ISO) ? (b)->fsd.iso_dir.##f \
: (b)->fsd.hsf_dir.##f)
/* "fetch primary descriptor value" */
#define FPDV(b, f, t) (((t) == ISO) ? (b)->iso_desc.##f \
: (b)->hsf_desc.##f)
/* user "application" program */
int
main(int argc, char *argv[])
{
struct directent openfile;
char pathname[80];
/* open the CDROM device */
if ((fsd.fd = open("/dev/ras2d", 0)) < 0) {
perror("cdromcat");
exit(1);
}
/* is there a filesystem we can understand here? */
if (iscdromfs(&rootent, &fsd) == 0) {
fprintf(stderr, "cdromcat: %s\n", fsd.name);
exit(1);
}
/* print the contents of the root directory to give user a start */
printf("Root Directory Listing:\n");
printdirentheader("/");
printdirents(&rootent, &fsd);
/* print files on demand from user */
for(;;){
/* prompt user for name to locate */
printf("Pathname to open? : ");
fflush(stdout);
/* obtain, if none, exit, else trim newline off */
if (fgets(pathname, sizeof(pathname), stdin) == NULL)
exit(0);
pathname[strlen(pathname) - 1] = '\0';
if (strlen(pathname) == 0)
exit(0);
/* lookup filename on CDROM */
if (lookup(&rootent, &openfile, pathname, &fsd)){
/* if a directory, format and list it */
if (ISO_BY(FDV(&openfile, flags, fsd.type))
& CD_DIRECTORY) {
printdirentheader(pathname);
printdirents(&openfile, &fsd);
}
/* if a file, print it on standard output */
else
extractdirent(&openfile, &fsd);
} else
printf("Not found.\n");
}
/* NOTREACHED */
}
/* ----------- Filesystem primatives ------------------- */
/* Check for the presence of a cdrom filesystem. If present, pass back
* parameters for initialization, otherwise, pass back error. */
int
iscdromfs(struct directent *dp, struct fs *fs) {
char buffer[CDROM_LSECSZ];
union voldesc *vdp = (union voldesc *) buffer;
/* locate at the beginning of the descriptor table */
lseek(fs->fd, VD_LSN*CDROM_LSECSZ, SEEK_SET);
/* walk descriptor table */
for(;;) {
unsigned char type;
/* obtain a descriptor */
read(fs->fd, buffer, sizeof(buffer));
/* determine ISO or HSF format of CDROM */
if (fs->type == 0) {
if (strncmp (vdp->iso_desc.id, ISO_STANDARD_ID,
sizeof(vdp->iso_desc.id)) == 0)
fs->type = ISO;
if (strncmp (vdp->hsf_desc.id, HSF_STANDARD_ID,
sizeof(vdp->hsf_desc.id)) == 0)
fs->type = HSF;
}
/* if determined, obtain root directory entry */
if (fs->type) {
type = ISO_BY(FPDV(vdp, type, fs->type));
if (type == VD_PRIMARY) {
bcopy (
(caddr_t) FPDV(vdp, root_directory_record, fs->type),
(caddr_t)dp, sizeof (union fsdir));
fs->lbs =
ISO_HWD(FPDV(vdp, logical_block_size, fs->type));
}
}
/* terminating volume */
if (type == VD_END)
break;
}
fs->name = cdromfmtnames[fs->type];
return (fs->type);
}
/* Obtain a "logical", i.e. relative to the directory entries beginning
* (or extent), block from the CDROM. */
int
getblkdirent(struct directent *dp, char *contents, long lbn, struct fs *fs) {
long filesize = ISO_WD(FDV(dp, size, fs->type)),
extent = ISO_WD(FDV(dp, extent, fs->type));
if (lbntob(fs, lbn) > roundup(filesize, fs->lbs))
return (0);
/* perform logical to physical translation */
(void) lseek(fs->fd, lbntob(fs, extent + lbn), SEEK_SET);
/* obtain block */
return (read(fs->fd, contents, fs->lbs) == fs->lbs);
}
/* Search the contents of this directory entry, known to be a directory itself,
* looking for a component. If found, return directory entry associated with
* the component. */
int
searchdirent(struct directent *dp, struct directent *fdp,
struct directent *compdp, struct fs *fs) {
struct directent *ldp;
long filesize = ISO_WD(FDV(dp, size, fs->type)),
comp_namelen = ISO_BY(FDV(compdp, name_len, fs->type)),
lbn = 0, cnt;
char *buffer = (char *) malloc(fs->lbs);
while (getblkdirent(dp, buffer, lbn, fs)) {
cnt = filesize > fs->lbs ? fs->lbs : filesize;
filesize -= cnt;
ldp = (struct directent *) buffer;
/* have we a record to match? */
while (cnt > sizeof (union fsdir)) {
long entlen, namelen;
/* match against component's name and name length */
entlen = ISO_BY(FDV(ldp, length, fs->type));
namelen = ISO_BY(FDV(ldp, name_len, fs->type));
if (entlen >= comp_namelen + sizeof(union fsdir) && namelen == comp_namelen
&& strncmp(FDV(ldp,name,fs->type),
FDV(compdp,name,fs->type), namelen) == 0) {
bcopy ((caddr_t)ldp, (caddr_t)fdp, entlen);
bcopy ((caddr_t)ldp, (caddr_t)compdp, entlen);
free(buffer);
return 1;
} else {
cnt -= entlen;
ldp = (struct directent *)
(((char *) ldp) + entlen);
}
}
if (filesize == 0) break;
lbn++;
}
free(buffer);
return 0;
}
/* Lookup the pathname by interpreting the directory structure of the CDROM
* element by element, returning a directory entry if found. Name translation
* occurs here, out of the null terminated path name string. This routine
* works by recursion. */
int
lookup(struct directent *dp, struct directent *fdp, char *pathname,
struct fs *fs) {
struct directent *ldp;
struct directent thiscomp;
char *nextcomp;
unsigned len;
/* break off the next component of the pathname */
if ((nextcomp = strrchr(pathname, '/')) == NULL)
nextcomp = strrchr(pathname, '\0');
/* construct an entry for this component to match */
ISO_BY(FDV(&thiscomp, name_len, fs->type)) = len = nextcomp - pathname;
bcopy(pathname, thiscomp.name, len);
/* attempt a match, returning component if found */
if (searchdirent(dp, fdp, &thiscomp, fs)){
/* if no more components, return found value */
if (*nextcomp == '\0')
return 1;
/* otherwise, if this component is a directory,
* recursively satisfy lookup */
else if (ISO_BY(FDV(dp, flags, fs->type)) & CD_DIRECTORY)
return (lookup(&thiscomp, fdp, nextcomp + 1, fs));
}
/* if no match return fail */
else
return(0);
}
/* --------------- object output routines for application ------------ */
/* Extract the entire contents of a directory entry and write this on
* standard output. */
void
extractdirent(struct directent *dp, struct fs *fs) {
long filesize = ISO_WD(FDV(dp, size, fs->type)),
lbn = 0, cnt;
char *buffer = (char *) malloc(fs->lbs);
/* iterate over all contents of the directory entry */
while (getblkdirent(dp, buffer, lbn, fs)) {
/* write out the valid portion of this logical block */
cnt = filesize > fs->lbs ? fs->lbs : filesize;
(void) write (1, buffer, cnt);
/* next one? */
lbn++;
filesize -= cnt;
if (filesize == 0) break;
}
free(buffer);
}
/* Print directory header */
void
printdirentheader(char *path) {
printf("Directory(%s):\n", path);
printf("Flags:\tsize date sysa name\n");
}
/* Print all entries in the directory. */
void
printdirents(struct directent *dp, struct fs *fs) {
struct directent *ldp;
long filesize = ISO_WD(FDV(dp, size, fs->type)),
lbn = 0, cnt;
char *buffer = (char *) malloc(fs->lbs);
while (getblkdirent(dp, buffer, lbn, fs)) {
long entlen, namelen;
cnt = filesize > fs->lbs ? fs->lbs : filesize;
filesize -= cnt;
ldp = (struct directent *) buffer;
entlen = ISO_BY(FDV(ldp, length, fs->type));
namelen = ISO_BY(FDV(ldp, name_len, fs->type));
/* have we a record to match? */
while (cnt > sizeof (union fsdir) && entlen && namelen) {
printdirent(ldp, fs);
/* next entry? */
cnt -= entlen;
ldp = (struct directent *) (((char *) ldp) + entlen);
entlen = ISO_BY(FDV(ldp, length, fs->type));
namelen = ISO_BY(FDV(ldp, name_len, fs->type));
}
if (filesize == 0) break;
lbn++;
}
free(buffer);
}
/* Print a directent on output, formatted. */
void
printdirent(struct directent *dp, struct fs *fs) {
unsigned extattlen;
unsigned fbname, name_len, entlen, enttaken;
/* mode flags */
prmodes(ISO_BY(FDV(dp, flags, fs->type)));
/* Note: this feature of HSF is not used because of lack of semantic def. */
#ifdef whybother
extattlen = ISO_BY(FDV(dp, ext_attr_length, fs->type));
if (extattlen)
printf(" E%3d", extattlen);
else
printf(" ");
#endif
/* size */
printf("\t%6d", ISO_WD(FDV(dp, size, fs->type)));
/* time */
printf(" %s",
cdrom_time((struct cdromtime *) FDV(dp, date, fs->type),fs->type));
/* compensate for reserved field used to word align directory entry */
entlen = ISO_BY(FDV(dp, length, fs->type));
name_len = ISO_BY(FDV(dp, name_len, fs->type));
enttaken = sizeof(union fsdir) + name_len;
if (enttaken & 1)
enttaken++;
fbname = ISO_BY(FDV(dp, name, fs->type));
entlen -= enttaken;
/* print size of CDROM Extensions field if present */
if (entlen)
printf(" %3d", entlen);
else
printf(" ");
/* finally print name. compensate for unprintable names */
if (name_len == 1 && fbname <= 1) {
printf("\t%s\n", (fbname == 0) ? "." : "..");
} else
printf("\t%s\n",
iso_astring(FDV(dp, name, fs->type), name_len));
};
/* print CDROM file modes */
prmodes(f) {
int i;
for(i=0; i < 8; i++) {
if(CD_FLAGBITS[i] == ' ')
continue;
if(f & (1<<i))
putchar(CD_FLAGBITS[i]);
else
putchar('-');
}
putchar(' ');
}
/* attempt to print a CDROM file's creation time */
char *
cdrom_time(struct cdromtime *crt, int type) {
struct tm tm;
static char buf[32];
char *fmt;
/* step 1. convert into a ANSI C time description */
tm.tm_sec = crt->sec;
tm.tm_min = crt->min;
tm.tm_hour = crt->hour;
tm.tm_mday = crt->day;
/* month starts with 1 */
tm.tm_mon = crt->month - 1;
tm.tm_year = crt->years;
tm.tm_isdst = 0;
/* Note: not all ISO-9660 disks have correct timezone field */
#ifdef whybother
/* ISO has time zone as 7th octet, HSF does not */
if (type == ISO) {
tm.tm_gmtoff = crt->tz*15*60;
tm.tm_zone = timezone(crt->tz*15, 0);
fmt = "%b %e %H:%M:%S %Z %Y";
} else
#endif
fmt = "%b %e %H:%M:%S %Y";
/* step 2. use ANSI C standard function to format time properly */
(void)strftime(buf, sizeof(buf), fmt, &tm);
return (buf);
}
static char __strbuf[200];
/* turn a blank padded character field into the null terminated strings
that POSIX/UNIX/WHATSIX likes so much */
char *iso_astring(char *sp, int len) {
bcopy(sp, __strbuf, len);
__strbuf[len] = 0;
for (sp = __strbuf + len - 1; sp > __strbuf ; sp--)
if (*sp == ' ')
*sp = 0;
return(__strbuf);
}
[LISTING FIVE]
bill 1 % cdromcat
Root Directory Listing:
Directory(/):
Flags: size date sysa name
-d---- 2048 Jul 27 12:49:16 1992 .
-d---- 2048 Jul 27 12:49:16 1992 ..
------ 394127 Jul 24 01:21:06 1992 0.ALL;1
------ 289565 Jul 24 01:21:06 1992 0.ASK;1
------ 2454 Jul 23 23:57:56 1992 0.DOC;1
-d---- 2048 Jul 27 12:50:06 1992 A2Z
-d---- 2048 Jul 27 12:50:07 1992 AI
-d---- 2048 Jul 27 12:50:07 1992 ARCHIVE
-d---- 2048 Jul 27 12:50:08 1992 CAD
-d---- 2048 Jul 27 12:50:08 1992 DATABASE
-d---- 2048 Jul 27 12:50:08 1992 DATACOMM
-d---- 2048 Jul 27 12:50:10 1992 DESKTOP
-d---- 2048 Jul 27 12:50:10 1992 DOCPREP
-d---- 2048 Jul 27 12:50:13 1992 GAME
-d---- 2048 Jul 27 12:50:13 1992 GRAPHICS
-d---- 2048 Jul 27 12:50:16 1992 LANGUAGE
-d---- 2048 Jul 27 12:50:27 1992 MATH
-d---- 2048 Jul 27 12:50:27 1992 MISC
-d---- 2048 Jul 27 12:50:28 1992 MUSIC
-d---- 2048 Jul 27 12:50:29 1992 OS
-d---- 2048 Jul 27 12:50:29 1992 PGM_TOOL
-d---- 2048 Jul 27 12:50:30 1992 SCIENCE
Pathname to open? : 0.DOC;1
Topic: (/)
Description: This is the top level directory of the PTF disc.
Notes:
<... contents of file ... >
Pathname to open? : OS
Directory(OS):
Flags: size date sysa name
-d---- 2048 Jul 27 12:50:29 1992 .
-d---- 2048 Jul 27 12:49:16 1992 ..
------ 593 Jul 23 23:53:40 1992 0.DOC;1
-d---- 2048 Jul 27 12:50:29 1992 CONDOR
-d---- 2048 Jul 27 12:50:29 1992 MACH
-d---- 2048 Jul 27 12:50:29 1992 MDQS
-d---- 2048 Jul 27 12:50:29 1992 PLAN_9
Pathname to open? : OS/PLAN_9
Directory(OS/PLAN_9):
Flags: size date sysa name
-d---- 2048 Jul 27 12:50:29 1992 .
-d---- 2048 Jul 27 12:50:29 1992 ..
------ 732 Jul 23 23:41:38 1992 0.DOC;1
------ 31 Jul 23 23:03:30 1992 0.LST;1
------ 230093 Jul 23 23:03:30 1992 PAPERS.ATZ;1
------ 472 Jul 23 23:03:30 1992 PAPERS.LTV;1
Pathname to open? :
bill 2 %